﻿using bfs.sms.sdmx.util.Control;
using bfs.sms.sdmx.util.Excel;
using bfs.sms.sdmx.util.SAS;
using bfs.sms.sdmx.util.Variable;
using SdmxMl.Common;
using SdmxMl.Manager;
using SdmxMl.Module;
using SdmxMl.Structure;
using SdmxMl.UC;
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace bfs.sms.sdmx.util
{
    /// <summary>
    /// Manage metadata collect and organize catalog information
    /// </summary>
    public class CatalogManager
    {
        #region Internal Members

        private SmManager sm;
        private List<TableOfContents> choices;
        private ImageList imageList;
        private MoveableTreeView treeView;
        private UcVarCatalog ucCatalog;

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor, giving controls to manage events
        /// </summary>
        /// <param name="userControlCatalog">the catalog control</param>
        /// <param name="tv">the treeview of the catalog</param>
        /// <param name="imgList">the imagelist to uses</param>
        public CatalogManager(UcVarCatalog userControlCatalog, SdmxMl.UC.MoveableTreeView tv, ImageList imgList)
        {
            // Initialize MetaManager with memory content
            RefreshCache();

            // reference controls
            ucCatalog = userControlCatalog;
            imageList = imgList;
            treeView = tv;

            // Availables choices for loading metadata
            choices = new List<TableOfContents>();
            choices.Add(TableOfContents.Catalog);
            choices.Add(TableOfContents.DataTables);
            choices.Add(TableOfContents.Codelists);

            InitTree();

            // Record event for key deletion
            tv.KeyUp += tv_KeyUp;

            tv.BeforeCheck += tv_BeforeCheck;

        }

        public void RefreshCache()
        {
            sm = ReferenceManager.GetSmContainerImage();
        }

        /// <summary>Init Tree with used module types as root </summary>
        private void InitTree()
        {
            // Reset
            treeView.Nodes.Clear();

            // Fill available modules containers
            foreach (TableOfContents toc in choices)
            {
                TreeNode node = new TreeNode(toc.ToString(), (int)toc, (int)toc);
                node.Tag = MyExtensions.SdmxType(toc);
                treeView.Nodes.Add(node);
            }
        }
        #endregion

        #region Language change support

        public void UpdateTreeText()
        {
            TreeUpdateText(treeView.Nodes);
        }

        private void TreeUpdateText(TreeNodeCollection nodes)
        {
            foreach (TreeNode nd in nodes)
            {
                if (!(nd.Tag is int))
                    nd.Text = nd.Tag.ToString();
                TreeUpdateText(nd.Nodes);
            }
        }

        #endregion

        #region Tree events

        void tv_BeforeCheck(object sender, TreeViewCancelEventArgs e)
        {
            // Refuse checking root nodes
            if (e.Node.Parent == null)
                e.Cancel = true;

            // Cancel parent check if any
            else if (e.Node.Parent != null && e.Node.Parent.Parent != null)
            {
                e.Node.Parent.Checked = false;
            }
        }

        /// <summary> Key Event to manage tree node delete </summary>
        /// <param name="sender"></param><param name="e"></param>
        void tv_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete && treeView.SelectedNode != null && treeView.SelectedNode.Parent != null)
            {
                treeView.SelectedNode.Remove();
            }
         }
        #endregion

        #region Load via Dialog

        /// <summary> Raise dialog for loading supported Meta types in tree </summary>
        /// <param name="toc">Meta types supported</param>
        public void LoadMeta(TableOfContents toc)
        {
            DlgModuleSelector aDlg = new DlgModuleSelector(toc, choices, string.Empty, imageList);

            // Attach event for adding a selected Meta module artefact
            aDlg.ModuleSelected += aDlg_ModuleSelected;
            aDlg.ShowDialog();

            // Dialog closed, detach method
            aDlg.ModuleSelected -= aDlg_ModuleSelected;
        }

        /// <summary> Event called when user select a maintenable artefact </summary>
        /// <param name="sender"></param><param name="e"></param>
        protected void aDlg_ModuleSelected(object sender, ModuleBookEventArgs e)
        {
            try
            {
                AddModule(MyExtensions.SdmxType(e.ContentModuleType), e.FullId);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Generator Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        #endregion

        #region Module adding

        /// <summary>
        /// Method for attaching an artefact module in tree
        /// </summary>
        /// <param name="arType">Type of SDMX artefact</param>
        /// <param name="fullId">identification of artefact</param>
        private void AddModule(SdmxArtefactType arType, string fullId)
        {
            foreach (TreeNode ndModule in treeView.Nodes)
            {
                if ((SdmxArtefactType)ndModule.Tag == arType && !AlreadyLoaded(ndModule, fullId))
                {
                    Artefact a = sm.GetArtefact(arType, new ArtefactRefBase(fullId), true);
                    if (a != null)
                        AttachInTree(a);
                }
            }
        }

        /// <summary>
        /// Get artefact currently selected in main tree of editor and add it if valid for catalog generation
        /// </summary>
        public void AddModuleFromMainTree()
        {
            if (ReferenceManager.TreeSdmx != null && ReferenceManager.TreeSdmx.SelectedNode != null)
            {
                SdmxMl.Common.Artefact a = GetSelectedArtefact(ReferenceManager.TreeSdmx.SelectedNode);
                if (a != null)
                {
                    // Concept or DSD
                    if (a.ArType == SdmxArtefactType.Concepts || a.ArType == SdmxArtefactType.KeyFamilies)
                    {
                        // If not yet in cache, add it
                        if (sm.GetArtefact(a.ArType, a) == null)
                            sm.PutArtefact(a.ArType, a);
                        AddModule(a.ArType, a.FullIdent);
                    }
                }
            }
        }

        /// <summary> Retrieves maintenable artefact from proposed tree node </summary>
        /// <param name="node">The node in main tree</param>
        /// <returns>Maintenable artefact or null if not found</returns>
        private SdmxMl.Common.Artefact GetSelectedArtefact(TreeNode node)
        {
            SdmxMl.Common.Artefact a = null;
            while (node != null && node.Tag != null)
            {
                if (node.Tag is SdmxMl.Common.Artefact)
                {
                    a = node.Tag as SdmxMl.Common.Artefact;
                    break;
                }
                node = node.Parent;
            }

            return a;
        }

        /// <summary> Check if specified module already present in tree </summary>
        /// <param name="ndModule">Root node of module type where to check</param>
        /// <param name="fullId">Artefact full Id</param>
        /// <returns>True if already attached in tree</returns>
        private bool AlreadyLoaded(TreeNode ndModule, string fullId)
        {
             foreach (TreeNode ndArtefact in ndModule.Nodes)
            {
                 Artefact a = ndArtefact.Tag as Artefact;
                 if (a != null && a.FullIdent == fullId)
                     return true;
            }
            return false;
        }       
        
        /// <summary> Attach artefact in corect domain of the tree, Add subConcepts if any </summary>
        /// <param name="a">the artefact to attach</param>
        private void AttachInTree(Artefact a)
        {
             foreach (TreeNode ndModule in treeView.Nodes)
            {
                if ((SdmxArtefactType)ndModule.Tag == a.ArType)
                {
                    TreeNode node = new TreeNode(a.ToString(), 10, 10);
                    node.Tag = a;
                    ndModule.Nodes.Add(node);
                    node.Checked = true;
                    node.EnsureVisible();
                    if (a.ArType == SdmxArtefactType.Concepts)
                    {
                        // Create 1st level Concepts (if more than one level)
                        ConceptSchemeType cst = a as ConceptSchemeType;
                        if (cst.ConceptList.HasHierarchy)
                        {
                            foreach (ConceptType c in cst.ConceptList)
                            {
                                if (string.IsNullOrEmpty(c.Parent))
                                {
                                    TreeNode nodeConcept = new TreeNode(c.ToString(), 10, 10);
                                    nodeConcept.Tag = c;
                                    node.Nodes.Add(nodeConcept);
                                }
                            }
                        }
                    }
                    break;
                }
             }
        }
        #endregion

        #region Catalog Generation

        private void UpdateReferences()
        {
            // Get edited meta
            RefreshCache();

            foreach (TreeNode ndModule in treeView.Nodes)
            {
                foreach (TreeNode nodeArtefact in ndModule.Nodes)
                {
                    Artefact aTag = nodeArtefact.Tag as Artefact;
                    Artefact a = null;
                    if ((a = sm.GetArtefact(aTag.ArType, aTag)) != null)
                    {
                        nodeArtefact.Tag = a;
                        if (a.ArType == SdmxArtefactType.Concepts)
                        {
                            ConceptSchemeType cst = a as ConceptSchemeType;
                            List<TreeNode> ndList = new List<TreeNode>();
                            foreach (TreeNode nodeConcept in nodeArtefact.Nodes)
                            {
                                // Check concept always exists
                                ConceptType c = cst.ConceptList.LocateItem(((ConceptType)nodeConcept.Tag).Id) as ConceptType;
                                if (c == null)
                                    ndList.Add(nodeConcept);
                                else
                                {
                                    nodeConcept.Tag = c;
                                }
                            }
                            foreach (TreeNode nd in ndList)
                                nodeArtefact.Nodes.Remove(nd);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Generate  Excel workbook for variable catalog and associated workbook for used codelists
        /// </summary>
        /// <param name="filepath">Path & name for Excel catalog</param>
        public void GenerateExcel()
        {
            SheetConstructor constructor = new SheetConstructor();
            UpdateReferences();
            constructor.ConstructList(sm, treeView.Nodes, ucCatalog.CatalogOptions);

            bool proceed = true;
            if (constructor.UnfinalizedArtefactList.Count > 0)
            {
                bfs.sms.sdmx.util.DlgUnfinalizedArtefacts aDlg = new DlgUnfinalizedArtefacts(constructor.UnfinalizedArtefactList);
                if (aDlg.ShowDialog() != DialogResult.OK)
                    proceed = false;
            }

            if (proceed)
            {
                SaveFileDialog saveFileDialog = new SaveFileDialog();
                saveFileDialog.Filter = "Excel |*.xls|Allfiles|*.*";
                saveFileDialog.FileName = string.Empty;
                if (saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {

                    string fname = System.IO.Path.GetFileName(saveFileDialog.FileName);
                    string dir = System.IO.Path.GetDirectoryName(saveFileDialog.FileName);
                    string codelistBookName = "CODES_" + fname;
                    string codePathName = dir + System.IO.Path.DirectorySeparatorChar + codelistBookName;

                    // Generates workbook of variable catalog with additional links to workbook of codelists
                    ExcelCatalog catalogBook = new ExcelCatalog("PPR", "BFS", codelistBookName);
                    catalogBook.Generate(constructor.SheetList, LangSupport.CurLang, ucCatalog.CatalogOptions);
                    catalogBook.Save(saveFileDialog.FileName);

                    // Generates Workbook of codelists
                    ExcelCodelists codelistBook = new ExcelCodelists("PPR", "BFS", constructor.QualityCodelist);
                    codelistBook.Generate(constructor.SheetList, LangSupport.CurLang, ucCatalog.CatalogOptions);
                    codelistBook.Save(codePathName);

                    System.Diagnostics.Process.Start("EXCEL.EXE", saveFileDialog.FileName);
                }
            }
        }

        /// <summary>
        /// Generate text file with SAS format instructions for all used codelist
        /// </summary>
        /// <param name="filepath">Path & name for text catalog</param>
        public void GenerateSasFormat()
        {
            SheetConstructor constructor = new SheetConstructor();
            constructor.ConstructList(sm, treeView.Nodes, ucCatalog.CatalogOptions);

            bool proceed = true;
            if (constructor.UnfinalizedArtefactList.Count > 0)
            {
                bfs.sms.sdmx.util.DlgUnfinalizedArtefacts aDlg = new DlgUnfinalizedArtefacts(constructor.UnfinalizedArtefactList);
                if (aDlg.ShowDialog() != DialogResult.OK)
                    proceed = false;
            }

            if (proceed)
            {
                SaveFileDialog saveFileDialog = new SaveFileDialog();
                saveFileDialog.Filter = "Text |*.txt|Allfiles|*.*";
                saveFileDialog.FileName = string.Empty;
                if (saveFileDialog.ShowDialog(Application.OpenForms[0]) == System.Windows.Forms.DialogResult.OK)
                {
                    FormatCatalog catalog = new FormatCatalog();
                    catalog.GenerateCodeFormat(constructor.SheetList, LangSupport.CurLang);
                    catalog.Save(saveFileDialog.FileName);
                    System.Diagnostics.Process.Start(saveFileDialog.FileName);
                }
            }
        }
        #endregion

        #region Set managment

        /// <summary> Load a set containing selections to place in catalog tree </summary>
        /// <param name="pathName">file holding set informations</param>
        public void LoadCurrentSet(string pathName)
        {
            InitTree();
            SmManager smSet = new SmManager();
            smSet.LoadSdmxDocument(pathName, true, null);
            foreach (Artefact a in smSet.GetArtefactList(SdmxArtefactType.Concepts))
            {
                AddModule(a.ArType, a.FullIdent);
                treeView.Nodes[0].LastNode.Expand();
                string[] arrChecked = a.Name.ToString().Split(',');
                foreach (string id in arrChecked)
                {
                    foreach (TreeNode nd in treeView.Nodes[0].LastNode.Nodes)
                    {
                        if ((nd.Tag as ConceptType).Id == id.Trim())
                        {
                            nd.Checked = true;
                            treeView.Nodes[0].LastNode.Checked = false;
                        }
                    }
                }
            }
            foreach (Artefact a in smSet.GetArtefactList(SdmxArtefactType.KeyFamilies))
            {
                AddModule(a.ArType, a.FullIdent);
            }
            foreach (Artefact a in smSet.GetArtefactList(SdmxArtefactType.CodeLists))
            {
                AddModule(a.ArType, a.FullIdent);
                break; // only one quality list
            }

        }

        /// <summary> Save current tree selections in a Set file </summary>
        /// <param name="pathName"></param>
        public void SaveCurrentSet(string pathName)
        {
            SmManager smSet = new SmManager();
            foreach (TreeNode nodeModule in treeView.Nodes)
            {
                foreach (TreeNode ndArtefact in nodeModule.Nodes)
                {
                    Artefact a = ndArtefact.Tag as Artefact;
                    Artefact setItem = null;
                    if (a is CodeListType)
                        setItem = new CodeListType();
                    else if (a is ConceptSchemeType)
                        setItem = new ConceptSchemeType();
                    else if (a is KeyFamilyType)
                        setItem = new KeyFamilyType();

                    if (setItem != null)
                    {
                        setItem.Id = a.Id;
                        setItem.Name = a.Name.Clone();
                        setItem.AgencyId = a.AgencyId;
                        setItem.Version = a.Version;
                        smSet.PutArtefact(setItem.ArType, setItem);
                        if (setItem is ConceptSchemeType)
                        {
                            setItem.Name.UpdateDefaultText(string.Empty);
                            foreach (TreeNode nogroup in ndArtefact.Nodes)
                            {
                                if (nogroup.Checked)
                                {
                                    ConceptType c = nogroup.Tag as ConceptType;
                                    setItem.Name.UpdateDefaultText(setItem.Name.ToString() + "," + c.Id);
                                }
                            }
                        }
                    }
                }
            }

            System.Xml.XmlDocument xDoc = smSet.GetAsDocument();
            xDoc.Save(pathName);

        }


        #endregion
    }
}
